/** \brief TM7707接口
 */

#include "tm7707.h"

//! \addtogroup TM7707
//! @{
// Communication register
enum {
    // Destination register
    REG_COMM = 0x00,    // Communication register.
    REG_SETUP = 0x10,   // Setting register.
    REG_HFILTER = 0x20, // Clock register.
    REG_DATA = 0x30,    // Data register.
    REG_TEST = 0x40,
    REG_LFILTER = 0x50,
    REG_ZERO_CH1 = 0x60, /* 偏移寄存*/
    REG_FULL_CH1 = 0x70, /* 满量程寄存器   */
    // Register
    WRITE = 0x00,  // Write register
    READ = 0x08,   // Read register
    // Channel index
    CH_1 = 0x00,   /* AIN1+  AIN1-     */
    CH_2 = 0x01,   /* AIN2+  AIN2-     */
    CH_3 = 0x02,   /* AIN1-  AIN1-     */
    CH_4 = 0x03,   /* AIN1-  AIN2-     */
    CH_MAX = 0x04, // Cannot write to device register.
};

// Setting register
enum {
    MD_NORMAL = (0 << 6),   /* 正常模式     */
    MD_CAL_SELF = (1 << 6), /* 自校准模 */
    MD_CAL_ZERO = (2 << 6), /* 校准0刻度模式  */
    MD_CAL_FULL = (3 << 6), /* 校准满刻度模*/

    GAIN_1 = (0 << 3),   /* 增益           */
    GAIN_2 = (1 << 3),   /* 增益           */
    GAIN_4 = (2 << 3),   /* 增益           */
    GAIN_8 = (3 << 3),   /* 增益           */
    GAIN_16 = (4 << 3),  /* 增益           */
    GAIN_32 = (5 << 3),  /* 增益           */
    GAIN_64 = (6 << 3),  /* 增益           */
    GAIN_128 = (7 << 3), /* 增益           */

    /* 无论双极性还是单极性都不改变任何输入信号的状态，它只改变输出数据的代码和转换函数上的校准*/
    BIPOLAR = (0 << 2),  /* 双极性输*/
    UNIPOLAR = (1 << 2), /* 单极性输*/

    BUF_NO = (0 << 1), /* 输入无缓冲（内部缓冲器不启用) */
    BUF_EN = (1 << 1), /* 输入有缓(启用内部缓冲 */

    FSYNC_0 = 0,
    FSYNC_1 = 1 /* 不启*/
};

// Clock register define.
enum {
    CLKDIS_0 = 0x00, /* 时钟输出使能 （当外接晶振时，必须使能才能振荡*/
    CLKDIS_1 = 0x10, /* 时钟禁止 （当外部提供时钟时，设置该位可以禁止MCK_OUT引脚输出时钟以省*/

    /*
        2.4576MHz（CLKDIV=0 ）或4.9152MHz （CLKDIV=1 ），CLK 应置 "
        1MHz （CLKDIV=0 ）或 2MHz   （CLKDIV=1 ），CLK 该位应置
    */
    CLK_4_9152M = 0x08,
    CLK_2_4576M = 0x00,
    CLK_1M = 0x04,
    CLK_2M = 0x0C,

    FS_50HZ = 0x00,
    FS_60HZ = 0x01,
    FS_250HZ = 0x02,
    FS_500HZ = 0x04,

    ZERO_0 = 0x00,
    ZERO_1 = 0x80
};

//! \brief 读取原始
int tm7707::get_raw() {
    _avilable = false;
    return _raw;
}

extern float TM7707_R_SCALE;

uint32_t TM7707_ADC_MAX;

//!	\brief 读取换算后工程
void tm7707::get_vout(float *val) {
    // ma4_20(_raw,0,TM7707_ADC_MAX,&_min,&_max,val);
}

//!	\brief 设定工程值范围最大
void tm7707::set_max(float *val) {
    _max = *val;
}

//! \brief 设定输入增益
void tm7707::set_gain(uint8_t val) {
    if (val < 8) {
        _gain = val << 3;
        _status &= 1;
    } else
        _status |= 1;
}

//! \brief 设定工程值范围最小
void tm7707::set_min(float *val) {
    _min = *val;
}

/************************************************************************/
/* Function Name:   busy.                                 */
/* Purpose:         Wait for device DRDY signal.                        */
/* Resources:       DRDY GPIO pin.                                      */
/* Params:          None.                                               */
/* Return:          None.                                               */
/* Limitation:      None.                                               */
/************************************************************************/
int tm7707::busy() {
    pinMode(_drdy, INPUT);
    if (digitalRead(_drdy))
        return 1;
    return 0;
}

/************************************************************************/
/* Function Name:   TM7707_SelfCalibration.                             */
/* Purpose:         Internal channel self-calibration.                  */
/* Resources:       SPI controler and GPIOs.                            */
/* Params:                                                              */
/*  @uiChannelIndex:    Channel index.                                  */
/* Return:          None.                                               */
/* Limitation:      None.                                               */
/************************************************************************/
void tm7707::TM7707_SelfCalibration(uint8_t uiChannelIndex) {
    // Write communication register to set setup register.
    hw_spi_rw_sim(_spi, REG_SETUP | WRITE | uiChannelIndex);
    // Write configuration register, start self-calibration.
    hw_spi_rw_sim(_spi, MD_CAL_SELF | _gain | _single | BUF_NO | FSYNC_0);
    // Wait for operation to complete(ablut 180ms).
}

int32_t tm7707::read(uint8_t uiChannelIndex) {
    int32_t rtnADValue = 0;
    // Write channel index.
    digitalWrite(_spi->cs, 0);

    hw_spi_rw_sim(_spi, REG_DATA | READ | uiChannelIndex);
    // Wait for the conversion to complete
    // Read ADC value.
    rtnADValue |= hw_spi_rw_sim(_spi, 0xFF);
    rtnADValue <<= 8;
    rtnADValue |= hw_spi_rw_sim(_spi, 0xFF);
    rtnADValue <<= 8;
    rtnADValue |= hw_spi_rw_sim(_spi, 0xFF);
    digitalWrite(_spi->cs, 1);

    return rtnADValue;
}

void tm7707::reset() {
    if (_reset > 0) {
        pinMode(_reset, OUTPUT);
        digitalWrite(_reset, 0);
        delay(1);
        digitalWrite(_reset, 1);
    } else {
        digitalWrite(_spi->cs, 0);
        hw_spi_rw_sim(_spi, 0);
        hw_spi_rw_sim(_spi, 0xff);
        hw_spi_rw_sim(_spi, 0xff);
        hw_spi_rw_sim(_spi, 0xff);
        hw_spi_rw_sim(_spi, 0xff);
        hw_spi_rw_sim(_spi, 0xff);
        hw_spi_rw_sim(_spi, 0xff);
    }
}

/************************************************************************/
/* Function Name:   TM7707_Initialize.                                  */
/* Purpose:         Initialize TM7707 device.                           */
/* Resources:       SPI controler and GPIO pins.                        */
/* Params:          None.                                               */
/* Return:          None.                                               */
/* Limitation:      None.                                               */
/************************************************************************/
void tm7707::init(uint8_t ch) {
    // Configure the clock registers
    uint16_t filter = 1000000 / 128 / _freq;
    if (use_vint) {
        hw_spi_rw_sim(_spi, REG_TEST | ch);
        hw_spi_rw_sim(_spi, 0x01); /* 使能内部基准*/
    } else {
        hw_spi_rw_sim(_spi, REG_TEST | ch);
        hw_spi_rw_sim(_spi, 0x00); /* 禁止内部基准*/
    }
    delayMicroseconds(10);
    hw_spi_rw_sim(_spi, REG_LFILTER | WRITE | ch); // Write communication register to set clook register.
    hw_spi_rw_sim(_spi, filter &
                        0xff);                 // External clock, frequency is 4.9152 MHz and the sampling frequency is 500Hz.
    hw_spi_rw_sim(_spi, REG_HFILTER | WRITE | ch); // Write communication register to set clook register.
    hw_spi_rw_sim(_spi, 0x10 | (filter >> 8) &
                               0xf); // External clock, frequency is 4.9152 MHz and the sampling frequency is 500Hz.
    delayMicroseconds(10);
    // TM7707_SelfCalibration(ch);
    // uint8_t val = _single | _gain | BUF_EN | FSYNC_0;
    // hw_spi_rw_sim(_spi, REG_SETUP | WRITE | ch); //选择设置寄存器写
    // hw_spi_rw_sim(_spi, val);                    /* gain = 4, bipolar mode, buffer off, clear FSYNC and perform a Self Calibration*/
    // delayMicroseconds(10);

    //    hw_spi_rw_sim(_spi,0x60|ch);
    //    hw_spi_rw_sim(_spi,0x1f);
    //    hw_spi_rw_sim(_spi,0x40);
    //    hw_spi_rw_sim(_spi,0x00);
    //
    //    hw_spi_rw_sim(_spi,0x70|ch);
    //    hw_spi_rw_sim(_spi,0x57);
    //    hw_spi_rw_sim(_spi,0x61);
    //    hw_spi_rw_sim(_spi,0xab);
}

int tm7707::loop() {
    switch (_step) {
        case 0:
            digitalWrite(_spi->cs, 0);
            reset();
            _step = 1;
            digitalWrite(_spi->cs, 1);
            last_tick = millis();
            break;
        case 1:
            if (millis() - last_tick > 50)
                _step = 2;
            if (!busy())
                _step = 2;
            break;
        case 2:
            _step = 3;
            break;
        case 3:
            _times++;
            digitalWrite(_spi->cs, 0);
            init(_ch);
            TM7707_SelfCalibration(_ch);
            digitalWrite(_spi->cs, 1);
            _step = 4;
            last_tick = millis();
            break;
        case 4:
            if (millis() - last_tick > 500)
                _step = 0;
            if (!busy()) {
                uint8_t val = _single | _gain | BUF_NO;
                hw_spi_rw_sim(_spi, REG_SETUP | WRITE | _ch); // 选择设置寄存器写
                hw_spi_rw_sim(_spi, val |
                                    FSYNC_1); /* gain = 4, bipolar mode, buffer off, clear FSYNC and perform a Self Calibration*/
                hw_spi_rw_sim(_spi, val |
                                    FSYNC_0); /* gain = 4, bipolar mode, buffer off, clear FSYNC and perform a Self Calibration*/

                _step = 5;
            }
            break;
        case 5:
            if (millis() - last_tick > 500)
                _step = 0;
            if (!busy()) {
                read(_ch);
                _step = 6;
            }
            break;
        case 6:
            if (millis() - last_tick > 500)
                _step = 0;
            if (!busy()) {
                _raw = read(_ch) - 0x800000;
                _avilable = true;
                _speed++;
                _ok++;
                _step = 3;
                last_tick = millis();
            }
            break;
        default:
            _step = 0;
            break;
    }
    return _avilable;
}

float TM7707_R_SCALE = 11;

void tm7707::begin() {
    _times = 0;
    _ok = 0;
    _freq = 50;
    _single = BIPOLAR;
    _gain = GAIN_2;
    TM7707_ADC_MAX = (float) 0x7fffff * 10 / (2.315 * TM7707_R_SCALE / 2);
    hw_spi_sim_init(_spi);
    _step = 0;
    _avilable = false;
}

tm7707::tm7707(spi_io_def *spi, int drdy, int reset, bool vref_int) {
    _spi = spi;
    _drdy = drdy;
    _reset = reset;
    use_vint = vref_int;
}

void tm7707::powerDown() {
    digitalWrite(_spi->cs, 0);
    hw_spi_rw_sim(_spi, REG_HFILTER | WRITE); // Write communication register to set clook register.
    hw_spi_rw_sim(_spi, 0x10);
    hw_spi_rw_sim(_spi, REG_COMM | WRITE | 4);
    digitalWrite(_spi->cs, HIGH);
    hw_spi_sim_uninit(_spi);
    pinMode(_drdy, INPUT_FLOATING);
    pinMode(_reset, INPUT_FLOATING);
}

void tm7707::set_channel(int ch) {
    _ch = ch;
}

//! @}
